<template>
  <div class="h-uploader h-uploader-image-container" ref="drag">
    <input ref="fileInput" type="file" :accept="accept?accept:''" :multiple="multiple"
           style="display: none;" @change="addFile">
    <Draggable :list="files" @update="updateValue">
      <transition-group name="list" appear>
        <div class="h-uploader-item" v-for="item in files" :key="item.uuid">
          <img v-if="item.type=='img'" :src="resolvePath(item.path)" style="width: 100%;height: 100%"/>
          <div style="text-align: center;padding:10px;width: 100%;overflow: hidden;text-overflow: ellipsis;"  v-else>
            {{ item.name }}
          </div>
          <Progress v-if="item.percent<100" :percent="item.percent" :strokeWidth="5" color="#ff5500"
                    style="position: absolute;bottom: 2px;width: 100%;">
          </Progress>
          <div class="h-uploader-actions">
            <span class="h-uploader-action" v-if="item.removing">
               <i class=" h-icon-spinner"></i>
            </span>
            <template v-else>
              <span class="h-uploader-action" v-if="item.type=='img'" @click="preview(item)">
                <i class="h-icon-search"></i>
              </span>
              <span class="h-uploader-action" v-else @click="download(item)">
                <i class="h-icon-inbox"></i>
              </span>
              <span class="h-uploader-action" @click="deleteFile(item)" v-if="!props.readonly">
                   <i class="h-space" style="width: 10px;"></i>
                  <i class="h-icon-trash"></i>
               </span>
            </template>
          </div>
        </div><!---->
      </transition-group>
    </Draggable>
    <div class="h-uploader-item add" v-if="!props.readonly&&props.limit!=files.length" @click="addFileClick">
      <i class="h-icon-plus"></i>
    </div>
  </div>
</template>

<script setup>
import {ref, reactive, onMounted, onBeforeUnmount, watch} from 'vue'
import {VueDraggableNext as Draggable} from 'vuedraggable'
import axios from 'axios'
import {ajax} from 'framework'
import {imagePreview, message} from 'HeyUI';

const CancelToken = axios.CancelToken;

const drag = ref(null), fileInput = ref(null)
const active = ref(false)
const files = ref([])
const accept = ref("")
const multiple = ref(false)

const emit = defineEmits(['update:modelValue', 'delete','upload'])
const props = defineProps({
  modelValue: {
    type: Array,
    default: []
  },
  readonly: {
    type: Boolean,
    default: false
  },
  //支持的后缀类型*.jpg,*.xx
  accept: {
    type: String
  },
  limit: {
    default: -1
  },
  uploadPath:{
    type:String,
    default:'file/upload'
  },
  deletePath:{
    type:String,
    default:'file/delete'
  },
  maxSize:{
    type:String,
    default:"10M"
  },
  dataType:{
    type:String,
    default:'object'
  }
})
//B,K,M,G
const sizeMap={
  B:'',
  K:'*1024',
  M:'*1024*1024',
  G:'*1024*1024*1024',
  T:'*1024*1024*1024*1024'
}
const maxFileSize=eval(props.maxSize.toUpperCase().replace(/[BKMG]/,s=>sizeMap[s]));

onMounted(() => {
  drag.value.addEventListener('drop', handleDrop)
  drag.value.addEventListener('dragleave', (e) => {
    active.value = false
    e.preventDefault()
  })
  drag.value.addEventListener('dragenter', (e) => {
    active.value = true
    e.preventDefault()
  })
  drag.value.addEventListener('dragover', (e) => {
    active.value = true
    e.preventDefault()
  })
  setItemsFromValues()
})
watch(()=>props.modelValue,setItemsFromValues)
accept.value=props.accept
multiple.value=props.limit>1||props.limit==-1

watch(()=>props.accept,(v)=>accept.value=v)
watch(()=>props.limit,(v)=>multiple.value=v>1||v==-1)

onBeforeUnmount(() => {
  drag.value.removeEventListener('drop', handleDrop)
})
let version=0
function setItemsFromValues() {
  let v = [];
  if(!props.modelValue){
    return
  }
  if(props.modelValue.__version==version){
    return
  }
  for (let i=0;i<props.modelValue.length;i++) {
    let item =props.modelValue[i]

    if(props.dataType!='object'){
      item={path:item}
    }
    let fileItem = {
      uuid: item.uuid?item.uuid:uuid(),
      path: item.path,
      rel: item,
      name:item.name,
      size:item.size
    }
    if (item.fileType) {
      fileItem.type = item.fileType
    } else if (/(jpg)|(png)|(jpeg)|(gif)|(svg)|(webp)$/.test(item.path.toLowerCase())) {
      fileItem.type = "img"
    } else {
      fileItem.type = "file"
    }

    v.push(fileItem)
  }
  files.value = v
}
function updateValue(){
  let ret=[]
  version++
  ret.__version=version;
  emit('change',files.value)
  if(props.dataType!='object'){
    for(let item of files.value){
      ret.push(item.path)
    }
    emit('update:modelValue',ret)
    return
  }
  for(let item of files.value){
    let it={}
    if(item.rel){
      it=item.rel
    }
    it.path=item.path
    it.name=item.name
    it.size=item.size
    it.fileType=item.type
    if(item.uuid){
      it.uuid=item.uuid
    }
    ret.push(it)
  }
  emit('update:modelValue',ret)
}
function uuid() {
  let s = [];
  const hexDigits = "0123456789abcdef";
  for (let i = 0; i < 36; i++) {
    s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
  }
  s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
  s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
  s[8] = s[13] = s[18] = s[23] = "-";
  return s.join("");
}

async function uploadFile(file) {
  if (props.readonly) {
    return;
  }
  if(file.size>maxFileSize){
    message("文件"+file.name+"超过"+props.maxSize)
    return
  }
  if (props.limit > -1) {
    if (files.value.length >= props.limit) {
      return
    }
  }
  let cancelToken;
  let fileItem = reactive({
    uuid: uuid(),
    percent: 0,
    name: file.name,
    size:file.size,
    path: '',
    removing: false,
    cancelUpload() {
      cancelToken.cancel()
    }
  })
  if (/image\/.*/.test(file.type)) {
    let fileReader = new FileReader();
    fileReader.onloadend = function () {
      if (fileReader.readyState == fileReader.DONE) {
        fileItem.type = "img"
        fileItem.path = fileReader.result
        //document.getElementById('img').setAttribute('src', fileReader.result);
      }
    };
    fileReader.readAsDataURL(file);
  } else {
    fileItem.type = "file"
  }
  const formData = new FormData()
  formData.append('file', file)
  // axios({
  //   url: props.uploadPath,
  //   method: 'POST',
  //   data: formData,
  //   cancelToken: new CancelToken(function executor(c) {
  //     cancelToken = c;
  //   }),
  //   onUploadProgress(progressEvent) {
  //     fileItem.percent = parseInt(progressEvent.loaded / progressEvent.total * 100)
  //   }
  // }).then((r) => {
  //   fileItem.path = r.data
  //   fileItem.percent = 100
  //   updateValue()
  // })
  files.value.push(fileItem)

  try{
    let ret = await axios({
      url: props.uploadPath,
      method: 'POST',
      data: formData,
      cancelToken: new CancelToken(function executor(c) {
        cancelToken = c;
      }),
      onUploadProgress(progressEvent) {
        fileItem.percent = parseInt(progressEvent.loaded / progressEvent.total * 100)
        //upload
      }
    })
    fileItem.path = ret.data
    fileItem.percent = 100
    emit('upload',fileItem)
    updateValue()
  }catch (e){
    console.error(e)
    fileItem.percent = 100
    removeFileItem(fileItem)
  }


}

const handleDrop = async (e) => {
  e.preventDefault()
  if (props.readonly) {
    return;
  }
  active.value = false
  for (let file of e.dataTransfer.files) {
    // if (file.type == '') {
    //   message({type: 'warn', text: '文件"' + file.name + '"因类型不符已被排除'})
    //   continue
    // }
    if (filterType(file.name)) {
      message({type: 'warn', text: '文件"' + file.name + '"因类型不符已被排除'})
      continue
    }
     await uploadFile(file)
  }
}

function filterType(name) {
  if (!props.accept) {
    return false
  }
  let type = name.substring(name.lastIndexOf(".")).toLowerCase();
  if (props.accept.indexOf(type) == -1) {
    return true
  }
  return false
}

function addFileClick() {
  fileInput.value.click()
}

async function addFile(e) {
  for (let file of e.target.files) {
    // if (file.type == '') {
    //   continue
    // }
    if (filterType(file.name)) {
      continue
    }
    await uploadFile(file)
  }
  e.target.value = null
}

function removeFileItem(item) {
  let vs = []
  for (let f of files.value) {
    if (item == f) {
      continue
    } else {
      vs.push(f)
    }
  }
  files.value = vs
  updateValue()
}

function deleteFile(item) {
  emit("delete",item)
  if (item.percent > -1 && item.percent < 100) {
    item.cancelUpload()
    removeFileItem(item)
  } else {
    if(/^https?:\/\//.test(item.path)){
      removeFileItem(item)
    }else{
      const formData = new FormData()
      formData.append('path', item.path)
      item.removing = true
      axios({
        url: props.deletePath,
        method: 'POST',
        data: formData
      }).then(r => {
        removeFileItem(item)
      }).finally(r => {
        item.removing = false
      })
    }
  }

}

function download(item) {
  //ajax下载
  ajax.download(resolvePath(item.path))
}

function preview(item) {
  let imgs = [];
  for (let it of files.value) {
    if (it.type == 'img') {
      imgs.push(resolvePath(it.path))
    }
  }
  let idx = 0
  for (let i = 0; i < imgs.length; i++) {
    let img = imgs[i]
    if (img == item.path) {
      idx = i
      break
    }
  }
  imagePreview(imgs, idx)
}
function resolvePath(path){
  //console.log(window._configs)
  if(path.startsWith('data:')){
    return path;
  }
  if(window._configs['PlatformConfig.FileSec']=='true'){
    if(path.indexOf('?')>-1){
      return path+"&_token="+sessionStorage._token
    }else{
      return path+"?_token="+sessionStorage._token
    }
  }
  return path
}
</script>

<style lang="less" scoped>
.file-icon {
  font-size: 25px;
  color: #d3d3d3;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.h-uploader-item {
  width: 120px;
  height: 120px;
  display: inline-flex;
  border-radius: 6px;
  box-sizing: border-box;
  overflow: hidden;
  border: 1px solid #dcdfe6;
  margin: 0 8px 8px 0;
  position: relative;
  justify-content: center;
  align-items: center;
  float: left;

  &.add {
    border: 1px dashed #cdd0d6;
    float: left;
    font-size: 28px;
    color: #909399;
  }

  &.add:hover {
    border-color: #409eff;
    color: #409eff;
  }

  .h-uploader-actions {
    position: absolute;
    width: 100%;
    height: 100%;
    left: 0;
    top: 0;
    cursor: default;
    display: inline-flex;
    justify-content: center;
    align-items: center;
    color: #fff;
    opacity: 0;
    font-size: 20px;
    background-color: rgba(0, 0, 0, .3);
    transition: opacity .3s;

    .h-uploader-action {
      cursor: pointer;
    }
  }

  .h-uploader-actions:hover {
    opacity: 1;
    cursor: move;
  }
}

.h-uploader-image-container {
  width: 100%;
}
</style>

<style>
/*设置列表的样式*/
.list-item {
  display: inline-block;
  margin-right: 10px;
}

/*设置列表transition-group的name为list的入场以及离场动画效果*/
.list-enter-active, .list-leave-active {
  transition: all .6s;
}

.list-enter, .list-leave-to
  /* .list-leave-active for below version 2.1.8 */
{
  opacity: 0;
  transform: translateY(30px);
}

/* 下面的 .v-move 和 .v-leave-active 配合使用，能够实现列表后续的元素，渐渐地移动过来的效果 */
.list-move {
  transition: all 0.6s ease;
}

.list-leave-active {
  position: absolute;
}
</style>
